﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace ODataTestGen.Wizard
{
    using System.IO;
    using System.Windows.Forms;
    using System.Xml.Linq;
    using ODataTestGen.Data;
    using ODataTestGen.OData;

    /// <summary>
    /// Interaction logic for SelectTestTemplates.xaml
    /// </summary>
    public partial class SelectTestTemplates : Page
    {
        public SelectTestTemplates(TestGenerationConfiguration configuration)
        {
            InitializeComponent();
            this.Configuration = configuration;
            this.buttonSaveCfg.IsEnabled = false;
            this.buttonAdd.IsEnabled = false;
            this.textBoxFolderLocation.Text = this.Configuration.startingDirectory;

            if (this.Configuration.admxLocation != null)
                this.textBoxFileLocation.Text = this.Configuration.admxLocation;

            else
                this.textBoxFileLocation.Text = Directory.GetCurrentDirectory() + "\\temp.admx";

            if (this.Configuration.defaultServiceRoot != null)
                this.textBoxServiceUri.Text = this.Configuration.defaultServiceRoot;
            else
            {
                this.textBoxServiceUri.Text = "Enter the service uri that the generated tests point to.";
                this.buttonNext.IsEnabled = false;                
            }
        }

        public TestGenerationConfiguration Configuration { get; set; }

        private void buttonNext_Click(object sender, RoutedEventArgs e)
        {
            this.Configuration.defaultServiceRoot = this.textBoxServiceUri.Text;
            TreeViewItem treeViewRoot = (TreeViewItem)this.treeViewTemplates.Items.GetItemAt(0);
            this.Configuration.Tests.Clear();
            enumerateTreeView(treeViewRoot);
            this.NavigationService.Navigate(new ChooseOutputDirectory { Configuration = this.Configuration });
        }

        private void buttonBack_Click(object sender, RoutedEventArgs e)
        {
            this.NavigationService.GoBack();
        }

        /// <summary>
        /// All test templates under the default starting directory are loaded into a trewview.
        /// </summary>
        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            if (this.treeViewTemplates.Items.Count > 0)
                this.treeViewTemplates.Items.Clear();
            var rootItem = this.CreateItem("Test Templates", null);
            string rootTemplateDirecotry = Path.Combine(this.Configuration.startingDirectory, "TestTemplates");
            this.PopulateDirectory(rootTemplateDirecotry, rootItem);            
            this.treeViewTemplates.Padding = new Thickness(3);
            this.treeViewTemplates.Items.Add(rootItem);
            ((System.Windows.Controls.CheckBox)rootItem.Header).IsChecked = true;
            if(this.Configuration.configurationFile != null)
                checkSelectedTest(rootItem);
        }

        private void PopulateDirectory(string directoryName, TreeViewItem rootItem)
        {
            var dirItem = this.CreateItem(directoryName, rootItem);
            this.PopulateChildren(dirItem, directoryName);
            rootItem.Items.Add(dirItem);
        }

        private void PopulateChildren(TreeViewItem parent, string parentDirectory)
        {
            if (!Directory.Exists(parentDirectory))
                return;
            foreach (string dir in Directory.GetDirectories(parentDirectory))
            {
                if (dir == Path.Combine(this.Configuration.startingDirectory, @"TestTemplates\Dependency Templates"))
                    continue;

                string name = Path.GetFileName(dir);
                TreeViewItem item = CreateItem(name, parent);
                
                parent.Items.Add(item);
                this.PopulateChildren(item, dir);
            }

            foreach (string file in Directory.GetFiles(parentDirectory, "*.tt").OrderBy(c => c))
            {
                string name = Path.GetFileNameWithoutExtension(file);
                TreeViewItem item = CreateItem(name, parent);
                parent.Items.Add(item);
            }
        }

        private TreeViewItem CreateItem(string name, TreeViewItem parent)
        {
            var checkbox = new System.Windows.Controls.CheckBox { Content = name };
            var item = new TreeViewItem
            {
                Header = checkbox,
                Padding = new Thickness(3),
            };

            checkbox.Checked += (sender, e) =>
                {
                    foreach (TreeViewItem i in item.Items)
                    {
                        ((System.Windows.Controls.CheckBox)i.Header).IsChecked = true;
                    }

                    this.UpdateParentCheckboxStatus(parent);
                };

            checkbox.Indeterminate += (sender, e) =>
                {
                    this.UpdateParentCheckboxStatus(parent);
                };

            checkbox.Unchecked += (sender, e) =>
                {
                    foreach (TreeViewItem i in item.Items)
                    {
                        ((System.Windows.Controls.CheckBox)i.Header).IsChecked = false;
                    }

                    this.UpdateParentCheckboxStatus(parent);
                };

            return item;
        }

        private void UpdateParentCheckboxStatus(TreeViewItem parent)
        {
            if (parent == null)
            {
                return;
            }

            var checkboxes = parent.Items.Cast<TreeViewItem>().Select(c => (System.Windows.Controls.CheckBox)c.Header);
            bool? newState = null;
            if (checkboxes.All(c => c.IsChecked.HasValue && c.IsChecked.Value))
            {
                newState = true;
            }
            if (checkboxes.All(c => c.IsChecked.HasValue && !c.IsChecked.Value))
            {
                newState = false;
            }
            System.Windows.Controls.CheckBox theCheckBox = (System.Windows.Controls.CheckBox)parent.Header;
            if (newState != theCheckBox.IsChecked)
            {
                theCheckBox.IsChecked = newState;
            }
        }

        /// <summary>
        /// This fucntion gets all the checked tests in the treeview and store them in current test configuration
        /// </summary>
        private void enumerateTreeView(TreeViewItem item)
        {
            if (item.HasItems)
            {
                foreach (TreeViewItem child in item.Items)
                {
                    enumerateTreeView(child);
                }
            }
            else
            {
                string path = getItemFullPath(item);
                if(!File.Exists(path))
                    return;
                System.Windows.Controls.CheckBox cb = (System.Windows.Controls.CheckBox)item.Header;
                if (cb.IsChecked.Value)
                {
                    TreeViewItem item_parent = (TreeViewItem)item.Parent;
                    while (item_parent.Parent != this.treeViewTemplates.Items[0])
                        item_parent = (TreeViewItem)item_parent.Parent;
                    string relatedPath = path.Replace(((System.Windows.Controls.CheckBox)item_parent.Header).Content.ToString(), "");
                    if (relatedPath.StartsWith("\\"))
                        relatedPath = relatedPath.Substring(1);
                    TestTemplateConfiguration tt = new TestTemplateConfiguration(cb.Content.ToString(), path, relatedPath);
                    this.Configuration.Tests.Add(tt);
                }
            }

        }

        /// <summary>
        /// This function saves the current configuration(both the selected entity sets and tests) to a local file.
        /// Parameter: outputFileName - The path where the configuration is stored.
        /// </summary>

        private void saveConfigurationFile(string outputFileName)
        {
            TreeViewItem treeViewRoot = (TreeViewItem)this.treeViewTemplates.Items.GetItemAt(0);
            this.Configuration.Tests.Clear();
            enumerateTreeView(treeViewRoot);

            XElement root = new XElement(ODataConstants.EdmxNamespace + "AnnotatedEdmx");
            XElement entitySets = new XElement(ODataConstants.EdmxNamespace + "EntityConfigurations");
            XElement selectedTest = new XElement(ODataConstants.EdmxNamespace + "SelectedTests");
            XElement defaultServiceUri = new XElement(ODataConstants.EdmxNamespace + "DefaultServiceUri");

            root.Add(defaultServiceUri);
            root.Add(entitySets);
            root.Add(selectedTest);

            defaultServiceUri.Value = this.textBoxServiceUri.Text;
            foreach (EntitySetConfiguration cfg in this.Configuration.Entities)
            {
                XElement entitySet = new XElement(ODataConstants.EdmxNamespace + "EntitySet");
                entitySet.SetAttributeValue("Name", cfg.Name);
                entitySet.SetAttributeValue("IsEnabled", cfg.IsEnabled);
                entitySet.SetAttributeValue("GetTest", cfg.GetTest);
                entitySet.SetAttributeValue("PostTest", cfg.PostTest);
                entitySet.SetAttributeValue("PutTest", cfg.PutTest);
                entitySet.SetAttributeValue("DeleteTest", cfg.DeleteTest);
                entitySets.Add(entitySet);
            }

            foreach (TestTemplateConfiguration tt in this.Configuration.Tests)
            {
                XElement test = new XElement(ODataConstants.EdmxNamespace + "Test");
                test.SetValue(tt.testName);
                test.SetAttributeValue("Path", tt.fullPath);
                test.SetAttributeValue("RelatedPath", tt.relatedPath);
                selectedTest.Add(test);
            }

            try
            {
                root.Save(outputFileName);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: failed to save the configuration file. \n\n" + ex);
                return;
            }
        }

        private void buttonBrowse_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.SaveFileDialog dlg = new System.Windows.Forms.SaveFileDialog();
            dlg.Filter = "Annotated Edmx File (*.admx)|*.admx";
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                this.textBoxFileLocation.Text = dlg.FileName;
            }
        }

        private void checkSelectedTest(TreeViewItem item)
        {
            if (item.HasItems)
            {
                foreach (TreeViewItem child in item.Items)
                    checkSelectedTest(child);
                return;
            }
            if (!isSelected(item))
            {
                ((System.Windows.Controls.CheckBox)item.Header).IsChecked = false;
            }
        }

        private bool isSelected(TreeViewItem item)
        {
            XElement selectedTests = this.Configuration.configurationFile.Element(ODataConstants.EdmxNamespace + "SelectedTests");

            foreach (XElement entity in selectedTests.Elements(ODataConstants.EdmxNamespace + "Test"))
            {
                if (entity.Value.Equals(((System.Windows.Controls.CheckBox)item.Header).Content))
                    return true;
            }
            return false;
        }

        private string getItemFullPath(TreeViewItem item)
        {
            TreeViewItem currentItem = item;
            string path = "";
            while (currentItem != this.treeViewTemplates.Items.GetItemAt(0))
            {
                path = Path.Combine(((System.Windows.Controls.CheckBox)currentItem.Header).Content.ToString(), path);
                currentItem = (TreeViewItem)currentItem.Parent;
            }
            path = path + ".tt";
            return path;
        }

        private void textBoxFileLocation_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (this.textBoxFileLocation.Text.Length == 0)
                this.buttonSaveCfg.IsEnabled = false;
            else
                this.buttonSaveCfg.IsEnabled = true;
        }

        private void textBoxFolderLocation_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (this.textBoxFolderLocation.Text.Length > 0)
                this.buttonAdd.IsEnabled = true;
            else
                this.buttonAdd.IsEnabled = false;
        }

        private void buttonBrosweFolder_Click(object sender, RoutedEventArgs e)
        {
            FolderBrowserDialog dlg = new FolderBrowserDialog();
            dlg.SelectedPath = this.Configuration.startingDirectory;
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                this.textBoxFolderLocation.Text = dlg.SelectedPath;
            }
        }

        private void buttonAdd_Click(object sender, RoutedEventArgs e)
        {
            if (!Directory.Exists(this.textBoxFolderLocation.Text))
            {
                MessageBox.Show("Error: the directory doesn't exist.");
                return;
            }

            this.PopulateDirectory(this.textBoxFolderLocation.Text, (TreeViewItem)this.treeViewTemplates.Items.GetItemAt(0));
        }

        private void buttonSaveCfg_Click(object sender, RoutedEventArgs e)
        {
            saveConfigurationFile(this.textBoxFileLocation.Text);
        }

        private void textBoxServiceUri_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (this.textBoxServiceUri.Text.Length == 0)
                this.buttonNext.IsEnabled = false;
            else
                this.buttonNext.IsEnabled = true;
        }
    }
}
